package UserInterface;

import Database.DatabaseOperator;
import com.bestvike.linq.IEnumerable;
import com.bestvike.linq.Linq;
import com.bestvike.linq.function.Func1;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.jetbrains.annotations.NotNull;
import vo.Program;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MainWindow
{
	private DatabaseOperator operator;
	private final ToggleGroup group = new ToggleGroup();
	public TableView<Program> showerTable;
	public Button nextButton;
	public Button lastButton;
	public Button searchButton;
	public Button spiderButton;
	public TextField searchField;
	public RadioButton faintMatch;
	public RadioButton halfAccuracyMatch;
	public RadioButton accuracyMatch;
	public TableColumn<Program, String> idColumn;
	public TableColumn<Program, String> countryColumn;
	public TableColumn<Program, String> universityColumn;
	public TableColumn<Program, String> schoolColumn;
	public TableColumn<Program, String> programNameColumn;
	public TableColumn<Program, String> homepageColumn;
	public TableColumn<Program, String> locationColumn;
	public TableColumn<Program, String> emailColumn;
	public TableColumn<Program, String> phoneNumberColumn;
	public TableColumn<Program, String> degreeColumn;
	public TableColumn<Program, String> deadlineWithAidColumn;
	public TableColumn<Program, String> deadlineWithoutAidColumn;
	private ObservableList<Program> programObservableList = FXCollections.observableArrayList();
	private int startIndex = 0;
	private int endIndex = 0;
	private int totalSize = 0;
	public MainWindow()
	{
		operator = new DatabaseOperator();
		totalSize = operator.count();
	}

	public static String TextFlowToString(TextFlow textFlow)
	{
		StringBuilder builder = new StringBuilder();
		for (Node node : textFlow.getChildren())
		{
			if (node instanceof Text)
			{
				builder.append(((Text) node).getText());
			}
		}
		return builder.toString();
	}

	@NotNull
	public static String getPatternHalfMatchWords(String text)
	{
		String returner = null;
		for (char i = 'A'; i < 'Z'; i++)
		{
			returner = text.replace("" + i, ".*" + (char) (i + ('a' - 'A'))).trim();
		}
		return returner;
	}
	@NotNull
	public static String[] getHalfMathWords(String text)
	{
		for (char i = 'A'; i < 'Z'; i++)
		{
			text = text.replace("" + i, " " + (char) (i + ('a' - 'A'))).trim();
		}
		return text.split(" ");
	}

	public void init()
	{
		initTable();
		initRadioButton();
	}

	public void initRadioButton()
	{
		faintMatch.setToggleGroup(group);
		accuracyMatch.setToggleGroup(group);
		halfAccuracyMatch.setToggleGroup(group);

	}

	public void initTable()
	{
		this.showerTable.setFixedCellSize(30);
		idColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("id"));
		countryColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("country"));
		universityColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("university"));
		schoolColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("school"));
		programNameColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("programName"));
		homepageColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("homepage"));
		locationColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("location"));
		emailColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("email"));
		phoneNumberColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("phoneNumber"));
		degreeColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("degree"));
		deadlineWithAidColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("deadlineWithAid"));
		deadlineWithoutAidColumn.setCellValueFactory(new PropertyValueFactory<Program, String>("deadlineWithoutAid"));
		showerTable.setItems(programObservableList);

		for (final Field field : MainWindow.class.getDeclaredFields())
		{
			if (field.getName().contains("Column"))
			{
				try
				{
					Object o = field.get(this);
					((TableColumn<Program, String>) o).setCellFactory(new Callback<TableColumn<Program, String>, TableCell<Program, String>>()
					{
						@Override
						public TableCell<Program, String> call(TableColumn<Program, String> param)
						{
							return new TableCell<Program, String>()
							{
								@Override
								protected void updateItem(String item, boolean empty)
								{
									setEditable(false);

									//									final String willingText = programObservableList.get(/*programObservableList.size
									//											() -1*/ 0).getProperty(field.getName().replace("Column", "")).trim();
									String willingText = null;
									try
									{
										willingText = item.trim();
									}
									catch (NullPointerException e)
									{
										willingText = "null";
									}
									//									System.out.println(item);
									//									System.out.println(willingText);
									final String searchString = searchField.getText().trim();
									if (accuracyMatch.isSelected())
									{
										if (willingText.equals(searchString))
										{
											Text text = new Text(willingText);
											text.setFill(Color.RED);
											TextFlow flow = new TextFlow(text);
											setGraphic(flow);
										} else
										{
											Text text = new Text(willingText);
											text.setFill(Color.BLACK);
											TextFlow flow = new TextFlow(text);
											setGraphic(flow);

										}
									} else if (halfAccuracyMatch.isSelected())
									{
										String[] halfMathWords = getHalfMathWords(searchString);
										IEnumerable<String> strings = Linq.asEnumerable(halfMathWords);
										strings.forEach(s ->
														{
															System.out.println(s);
                                                        });
										HashSet<String> set = new HashSet<>();
										String replace = willingText;
										TextFlow flow = new TextFlow();
										for (String halfMathWord : halfMathWords)
										{
											set.add(halfMathWord);
											replace = replace.toLowerCase().replace(halfMathWord, "_" + halfMathWord + "_");
										}
										for (String s : replace.split("_"))
										{
											Text text = new Text(s);
											if (set.contains(s))
											{
												text.setFill(Color.RED);
											}
											else
											{
												text.setFill(Color.BLACK);
											}
											flow.getChildren().add(text);
										}
										setGraphic(flow);
									}
									else if (faintMatch.isSelected())
									{
										String faintPattern = getFaintPattern(searchString);

										String replace = willingText;
										Pattern pattern = Pattern.compile(faintPattern);
										//								pattern.matcher().groupCount();
										TextFlow textFlow = new TextFlow();
										Matcher matcher = pattern.matcher(replace);
										if (matcher.find())
										{
											for (int index = 1; index <= matcher.groupCount(); index++)
											{
												if(Objects.equals(matcher.group(index), "")){
													continue;
												}
//												System.out.println(matcher.group(index) + ": " + (index % 2 == 0 ?
//														"red" : "black"));

												Text text = new Text(matcher.group(index));
												text.setFill(index % 2 == 0 ? Color.RED : Color.BLACK);
												textFlow.getChildren().add(text);

											}
										} else
										{
											Text text = new Text(replace);
											text.setFill(Color.BLACK);
											textFlow.getChildren().add(text);
										}
										setGraphic(textFlow);

									}
								}
							};


						}
					});
				}
				catch (IllegalAccessException e)
				{

				}
			}
		}
	}

	private TableCell<Program, TextFlow> accuracyHighlight(TableColumn<Program, TextFlow> param, String text)
	{
		if (Objects.equals(text, param.getText()))
		{
			TextFlow flow = new TextFlow();
			Text text1 = new Text(text);
			text1.setFill(Color.RED);
			flow.getChildren().add(text1);
		}
		return null;
	}

	private TableCell<Program, TextFlow> halfAccuracyHighlight(TableColumn<Program, TextFlow> param, String text)
	{
		return null;
	}

	private TableCell<Program, TextFlow> faintHighlight(TableColumn<Program, TextFlow> param, String text)
	{
		return null;
	}

	String text = "";
	public void searchButton_OnClick(MouseEvent mouseEvent)
	{
		this.startIndex = 0;
		this.endIndex = 15;
		text = this.searchField.getText().trim();
		showTableView(text);
	}

	public void showTableView(String text)
	{
		this.programObservableList.clear();
		this.programObservableList.removeAll();
		if (text.trim().equals(""))
		{
			this.programObservableList.clear();
			this.programObservableList.removeAll();
			return;
		}

		if (accuracyMatch.isSelected())
		{
			fullMatchInDb(text);
		}
		else if (halfAccuracyMatch.isSelected())
		{
			halfMatchInDb(text);
		}
		else
		{
			faintMatchInDb(text);
		}
	}

	public void nextButton_OnClick(MouseEvent mouseEvent)
	{
		if(endIndex < this.totalSize)
		{
			startIndex += 10;
			endIndex += 10;
			showTableView(text);
		}
	}

	public void lastButton_OnClick(MouseEvent mouseEvent)
	{
		if(startIndex > 0)
		{
			startIndex -= 10;
			endIndex -= 10;
			showTableView(text);
		}
	}

	public void faintMatchInDb(String text)
	{
		String compiler = getFaintPatternForMySQL(text);
		for (Field field : Program.class.getDeclaredFields())
		{
			String property = getSqlColumnName(field);

			for (Program program : operator.getTopById(startIndex, endIndex, property, compiler.toLowerCase()))
			{
//				if (program.getProperty(field.getName()).toLowerCase().trim().matches(compiler.toLowerCase()))
//				{
				if (program!= null && program.getSchool()!= null)
				{
					IEnumerable<Program> programs = Linq.asEnumerable(programObservableList);
					if(!programs.any(program1 -> program1.getId().equals(program.getId())))
					{
						this.programObservableList.add(program);
					}
				}
//				}
			}
		}
	}

	public String getSqlColumnName(Field field)
	{
		return field.getName().replace("N", "_N").replace("W", "_W").replace("A", "_A");
	}

	@NotNull
	public String getFaintPattern(String text)
	{
		StringBuilder compiler = new StringBuilder("(?i)(.*?)");
		char[] charArray = text.toCharArray();
		for (int i = 0; i < charArray.length; i++)
		{
			char t = charArray[i];
			compiler.append("(").append(t).append(")");
			if (i != charArray.length - 1)
			{
				compiler.append("(.*?)");
			} else
			{
				compiler.append("(.*)");
			}
		}
		System.out.println(compiler);
		return compiler.toString();
	}
	@NotNull
	public String getFaintPatternForMySQL(String text)
	{
		StringBuilder compiler = new StringBuilder();
		char[] charArray = text.toCharArray();
		for (int i = 0; i < charArray.length; i++)
		{
			char t = charArray[i];
			compiler.append(t);
			if (i != charArray.length - 1)
			{
				compiler.append(".*");
			}
		}
		System.out.println(compiler);
		return compiler.toString();
	}
	public void halfMatchInDb(String text)
	{
		//全大写
		if (Objects.equals(text.toUpperCase(), text))
		{
			fullMatchInDb(text);
		} else
		{
			String pattern = getPatternHalfMatchWords(text);
			Field[] declaredFields = Program.class.getDeclaredFields();
			Program[] all;
			for (Field declaredField : declaredFields)
			{
				String sqlColumnName = getSqlColumnName(declaredField);
				all = operator.getTopById(startIndex,
										  endIndex,
										  sqlColumnName,
										  pattern);
				this.programObservableList.addAll(all);
			}
//			String[] split = getHalfMathWords(text);
//			Field[] declaredFields = Program.class.getDeclaredFields();
//			Program[] all;
//			for (Program program : all)
//			{
//				label:
//				for (Field declaredField : declaredFields)
//				{
//					String name = declaredField.getName();
//					for (String s : split)
//					{
//						if (program.getProperty(name).toLowerCase().contains(s))
//						{
//							this.programObservableList.add(program);
//							break label;
//
//						}
//					}
//				}
//			}

		}
	}

	public void fullMatchInDb(String text)
	{
			for (Field field : Program.class.getDeclaredFields())
			{
				for (Program program : operator.getTopById(startIndex,
														   endIndex,
														   getSqlColumnName(field),
														   "^" + text + "$"))
				{
					if(program != null && program.getSchool()!= null)
						this.programObservableList.addAll();

				}

			}

	}

	public void spiderButton_OnClick(MouseEvent mouseEvent) {
		Stage stage = new Stage();
		FXMLLoader loader = null;
		Pane pane = new Pane();
		loader = new FXMLLoader(getClass().getClassLoader().getResource("Fxml/SpiderWindow.fxml"));
		try {
			pane = loader.load();
		} catch (IOException e) {
			e.printStackTrace();
		}
		SpiderWindow controller = loader.getController();
		controller.init(operator);
		Scene scene = new Scene(pane);
		stage.setScene(scene);
		stage.show();
	}
}
